package buildinjava.io;

import com.google.inject.Singleton;
import java.io.File;
import java.io.FileFilter;

/**
 * Returns the intersection union union of file filters.
 * 
 * @author Gili Tzabari
 */
@Singleton
public final class FileFilters
{
	private static final FileFilter acceptAllFilter = new FileFilter()
	{
		@Override
		public boolean accept(final File pathname)
		{
			return true;
		}
	};
	private static final FileFilter rejectAllFilter = new FileFilter()
	{
		@Override
		public boolean accept(final File pathname)
		{
			return false;
		}
	};
	private static final FileFilter isFileFilter = new FileFilter()
	{
		@Override
		public boolean accept(final File file)
		{
			return file.isFile();
		}
	};
	private static final FileFilter isDirectoryFilter = new FileFilter()
	{
		@Override
		public boolean accept(final File file)
		{
			return file.isDirectory();
		}
	};
	private static final FileFilter emptyDirectoriesFilter = new FileFilter()
	{
		@Override
		public boolean accept(final File file)
		{
			if (!file.isDirectory())
				return false;
			for (File nestedFile: file.listFiles())
				if (!accept(nestedFile))
					return false;
			return true;
		}
	};

	/**
	 * Prevent construction.
	 */
	private FileFilters()
	{
	}

	/**
	 * Returns the union of file filters.
	 *
	 * @param filters the file filters
	 * @return a union of file filters
	 */
	public static FileFilter union(final FileFilter... filters)
	{
		return new FileFilter()
		{
			@Override
			public boolean accept(final File file)
			{
				for (FileFilter filter: filters)
					if (filter.accept(file))
						return true;
				return false;
			}
		};
	}

	/**
	 * Returns the results accepted by the first filter minus the results of the second.
	 *
	 * @param first the first filter
	 * @param second the second filter
	 * @return the first filter's results minus the second
	 */
	public static FileFilter remove(final FileFilter first, final FileFilter second)
	{
		return intersection(first, negation(second));
	}

	/**
	 * Returns the intersection of file filters.
	 *
	 * @param filters the file filters
	 * @return an intersection of file filters
	 */
	public static FileFilter intersection(final FileFilter... filters)
	{
		if (filters.length == 1)
			return filters[0];
		return new FileFilter()
		{
			@Override
			public boolean accept(final File file)
			{
				for (FileFilter filter: filters)
					if (!filter.accept(file))
						return false;
				return true;
			}
		};
	}

	/**
	 * Returns a file filter that returns the opposite result of another filter.
	 *
	 * @param filter the filter to negate
	 * @return the negation of the file filter
	 */
	public static FileFilter negation(final FileFilter filter)
	{
		return new FileFilter()
		{
			@Override
			public boolean accept(final File file)
			{
				return !filter.accept(file);
			}
		};
	}

	/**
	 * Returns a filter that accepts any file or directory.
	 *
	 * @return a filter that accepts any file or directory
	 */
	public static FileFilter acceptAll()
	{
		return acceptAllFilter;
	}

	/**
	 * Returns a filter that rejects all files and directories.
	 *
	 * @return a filter that rejects all files and directories
	 */
	public static FileFilter rejectAll()
	{
		return rejectAllFilter;
	}

	/**
	 * Returns a filter that accepts files.
	 *
	 * @return a filter that accepts files
	 */
	public static FileFilter isFile()
	{
		return isFileFilter;
	}

	/**
	 * Returns a filter that accepts directories.
	 *
	 * @return a filter that accepts directories
	 */
	public static FileFilter isDirectory()
	{
		return isDirectoryFilter;
	}

	/**
	 * Returns a filter that accepts empty directories.
	 *
	 * @return a filter that accepts empty directories
	 */
	public static FileFilter emptyDirectories()
	{
		return emptyDirectoriesFilter;
	}
}
